unit GLGUILines;

interface

uses
  Classes,
  Types,
  GLUtils,
  GLGUI2dSprite,
  OpenGLTokens,
  OpenGLAdapter,
  VectorTypes,
  VectorGeometry,
  VectorLists,
  GLRenderContextInfo,
  BaseClasses,
  GLState,
  GLSGenerics,
  GLGUIBase,
  GLContext,
  GLGUIShaders,
  GLGUIUTils,
  GLColor,
  GLGUIMouseControl,
  GLGUIQuad;

type
  TGLGUILineDrawStyle = (ldsContinuous, ldsDoted, ldsDashed);
  TVector2fList = GList<TVector2f>;
  
  TGLGUIBasicLine = class(TGLGUI2DSpriteCustom)
  protected
    FViewPortSize: TVector2f;
    FStartNode: TVector2f;
    FFinishNode: TVector2f;
    FStartRenderNode: TVector2f;
    FFinishRenderNode: TVector2f;
    FNeedUpdate: Boolean;
    procedure TransformCoords(AX, AY: Single); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    procedure DoRender(var ARci: TRenderContextInfo;
      ARenderSelf, ARenderChildren: Boolean); override;
    procedure StartNode(AX, AY: Single);
    procedure FinishNode(AX, AY: Single);
    property Start: TVector2f read FStartNode;
    property Finish: TVector2f read FFinishNode;
    property NeedUpdate: Boolean read FNeedUpdate write FNeedUpdate;
  end;

  TGLGUIPolyLine = class(TGLGUI2DSpriteCustom)
  private
    FDrawStyle: TGLGUILineDrawStyle;
    FRenderNodes: Array of TVector2f;
    FNodesQuee: TVector2fList;
    FNeedUpdate: Boolean;

    procedure Transform(AWidth, AHeight: Single);
  public
    constructor Create(AOwner: TComponent); override;
    procedure AddNode(AX, AY: Single);
    procedure SetNodes(ANodes: TVector2fList);
    procedure Clear;
    procedure DoRender(var ARci: TRenderContextInfo;
      ARenderSelf, ARenderChildren: Boolean); override;

    property DrawStyle: TGLGUILineDrawStyle read FDrawStyle write FDrawStyle;
  end;

implementation

uses
  GLGUILayer;

constructor TGLGUIBasicLine.Create(AOwner: TComponent);
begin
  inherited;
  FNeedUpdate := True;
end;

procedure TGLGUIBasicLine.DoRender(var ARci: TRenderContextInfo;
  ARenderSelf, ARenderChildren: Boolean);
begin
  if FNeedUpdate then
  begin
    with ARci.viewPortSize do
      TransformCoords(cX, cY);
    FNeedUpdate := False;
  end;

  if ARenderSelf then
  begin
    GUILineShader.UseProgramObject;

    GUILineShader.Uniform4f['uModulateColor'] := FColor.Color;

    GL.Disable(GL_LINE_STIPPLE);
    GL.Begin_(GL_LINES);
    GL.Vertex2fv(@FStartRenderNode);
    GL.Vertex2fv(@FFinishRenderNode);
    GL.End_();

    GUILineShader.EndUseProgramObject;

    FViewPortSize[0] := ARci.viewPortSize.cX;
    FViewPortSize[1] := ARci.viewPortSize.cY;
  end;
end;

procedure TGLGUIBasicLine.TransformCoords(AX, AY: Single);
begin
  FStartRenderNode := ScreenToOpenGL(FStartNode[0], FStartNode[1], AX, AY);
  FFinishRenderNode := ScreenToOpenGL(FFinishNode[0], FFinishNode[1], AX, AY);
end;

procedure TGLGUIBasicLine.StartNode(AX, AY: Single);
begin
  FStartNode := Vector2fMake(AX, AY);
  FNeedUpdate := True;
end;

procedure TGLGUIBasicLine.FinishNode(AX, AY: Single);
begin
  FFinishNode := Vector2fMake(AX, AY);
  FNeedUpdate := True;
end;

constructor TGLGUIPolyLine.Create(AOwner: TComponent);
begin
  inherited;
  FNeedUpdate := True;
  FNodesQuee := TVector2fList.Create;
  FDrawStyle := ldsContinuous;
end;

procedure TGLGUIPolyLine.Transform(AWidth, AHeight: Single);
var
  i: Integer;
begin
  SetLength(FRenderNodes, 0);
  for i := 0 to FNodesQuee.Count - 1 do
  begin
    SetLength(FRenderNodes, i+1);
    FRenderNodes[i] := ScreenToOpenGL(FNodesQuee[i][0], FNodesQuee[i][1], AWidth, AHeight);
  end;
end;

procedure TGLGUIPolyLine.AddNode(AX, AY: Single);
begin
  FNodesQuee.Add(Vector2fMake(AX, AY));
  FNeedUpdate := True;
end;

procedure TGLGUIPolyLine.SetNodes(ANodes: TVector2fList);
var
  i: Integer; 
begin
  FNodesQuee.Clear;
  for i := 0 to ANodes.Count - 1 do
  begin
    FNodesQuee.Add(ANodes[i]);
  end;
  FNeedUpdate := True;
end;

procedure TGLGUIPolyLine.Clear;
begin
  FNodesQuee.Clear;
  FNeedUpdate := True;
end;

procedure TGLGUIPolyLine.DoRender(var ARci: TRenderContextInfo;
  ARenderSelf, ARenderChildren: Boolean);
var
  i: Integer;
begin
  if FNeedUpdate then
  begin
    with ARci.viewPortSize do
    begin
      Transform(cX, cY);
      FNeedUpdate := False;
    end;
  end;

  if ARenderSelf then
  begin
    GUILineShader.UseProgramObject;

    GUILineShader.Uniform4f['uModulateColor'] := FColor.Color;

    case FDrawStyle of
      ldsContinuous:
        GL.Disable(GL_LINE_STIPPLE);
      ldsDoted:
      begin
        GL.Enable(GL_LINE_STIPPLE);
        GL.LineStipple (1, $0101);
      end;
      ldsDashed:
      begin
        GL.Enable(GL_LINE_STIPPLE);
        GL.LineStipple (1, $00FF);
      end;
    end;

    GL.Begin_(GL_LINE_STRIP);
    for i := 0 to Length(FRenderNodes)-1 do
      GL.Vertex2fv(@FRenderNodes[i]);
    GL.End_();

    GUILineShader.EndUseProgramObject;
  end;
end;

end.
